#include "dynamicEntity.h"

dynamicEntity::dynamicEntity(glm::vec3 _Pos, float _r, float _speed,
		std::string _Name, std::string _modelFName, ModelPool *_mPool,
		PathFinder *_pathfinder, Terrain::Terrain *ter) :
		Entity(_Pos, _r, _Name, _modelFName, _mPool, _pathfinder)
{
	type = OBJ_DYNAMIC;
	parentType = OBJ_ENTITY;

	this->ter = ter;

	shaderID = Shader::sPool->getProgramID("building");
	shadowShaderID = Shader::sPool->getProgramID("building_sh");

	MoveVector = std::list<glm::vec2>();

	lastReserved = glm::vec2((int) Pos.x, (int) Pos.z);
	refPathCycles = 0;
	basespeed = _speed;
	speed = _speed;

	resFlag = true;

	Moving = false;
	visible = false;

	BuildUnitBar(modelFName + "_char.png");
}

dynamicEntity::~dynamicEntity(void)
{
}

void dynamicEntity::move()
{
	//resFlag is handling walkability cleaning. No dead nodes anymore! :D
	//drawPath(MoveVector);

	/*
	 refPathCycles ++;
	 if(refPathCycles == 16)
	 {
	 glm::vec2 tempv;
	 if(MoveVector.size() > 2)
	 {
	 tempVector = MoveVector;
	 tempVector.pop_front();

	 MoveVector = pathfinder->getPath(MoveVector.front(), lastReserved);
	 while(MoveVector.front() == lastReserved)
	 {
	 tempVector.pop_front();
	 MoveVector = pathfinder->getPath(tempVector.front(), lastReserved);
	 refPathCycles = 0;
	 if(tempVector.size() == 0)
	 break;
	 }


	 }
	 else
	 {
	 MoveVector = pathfinder->getPath(tempVector.front(), lastReserved);
	 refPathCycles = 0;
	 }
	 }
	 */
	if (newMove)
	{
		if ((pathfinder->ReservePosition((int) MoveVector.back().x,
				(int) MoveVector.back().y, this)) != 1)
		{
			pathfinder->getPath(MoveVector.front(), lastReserved);
			resFlag = true;
		}
		pathfinder->FreePosition(lastReserved.x, lastReserved.y);
		lastReserved = MoveVector.back();
		newMove = false;
		resFlag = false;

	}

	float c = MoveVector.back().x - Pos.x;
	float b = MoveVector.back().y - Pos.z;
	float rRad = 0.0;

	if (MoveVector.back().y < Pos.z)
	{
		rRad = atan(c / b) + PI;
		r = (rRad * 180.0 / PI);
	}
	else
	{
		rRad = atan(c / b);
		r = rRad * 180.0 / PI;
	}

	// set new position
	if (sqrt(pow(c, 2) + pow(b, 2)) < speed)
	{
		//position buffer
		MoveVector.pop_back();
		// UNIT position reservation. Unit reservers next position.
		// If not possible, means that postition taken in next step, so search anoter way
		if (MoveVector.size() > 0)
		{
			if (pathfinder->ReservePosition((int) MoveVector.back().x,
					(int) MoveVector.back().y, this) == 1)
			{

				pathfinder->FreePosition(lastReserved.x, lastReserved.y);
				lastReserved = MoveVector.back();
			}
			else if (pathfinder->ReservePosition((int) MoveVector.back().x,
					(int) MoveVector.back().y, this) == 69)
			{
				MoveVector.clear();
			}
			else
			{
				resFlag = true;
				//if position is taken, find another path to destination, call pathfinding again.
				if (MoveVector.size() > 2)
				{
					tempVector = MoveVector;
					tempVector.pop_front();
					MoveVector = pathfinder->getPath(MoveVector.front(), lastReserved);
					if (MoveVector.front() == lastReserved)
					{
						MoveVector = pathfinder->getPath(tempVector.front(), lastReserved);
					}
					refPathCycles = 0;
				}
				else
				{
					MoveVector = pathfinder->getPath(MoveVector.front(), lastReserved);
					refPathCycles = 0;
				}
			}
		}
		else
		{
			pathfinder->ReservePosition(lastReserved.x, lastReserved.y, this);
		}

	}
	else
	{
		Pos.x = Pos.x + (speed) * sin(rRad);
		Pos.z = Pos.z + (speed) * cos(rRad);
		Pos.y = ter->getInterYVal(glm::vec2(Pos.x, Pos.z));
	}

	Moving = true;
}

void dynamicEntity::drawPath(std::list<glm::vec2> MVector)
{

	GLuint shaderIDP = Shader::sPool->getProgramID("cube");

	glm::mat4 ModelMatrix = glm::mat4(1.0f);
	glm::mat4 ViewMatrix = World::world->getView();
	glm::mat4 MVP = World::world->getProjection() * ViewMatrix * ModelMatrix;

	std::list<glm::vec2>::iterator i;
	// start shader program

	glUseProgram(shaderIDP);

	// send matrix to shader
	glUniformMatrix4fv(glGetUniformLocation(shaderIDP, "MVP"), 1, GL_FALSE,
			&MVP[0][0]);
	glUniform3f(glGetUniformLocation(shaderIDP, "color"), 1, 1, 0);

	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	glBegin(GL_LINES);

	for (i = MVector.end(); i != MVector.begin(); i--)
	{

		glVertex3f(i->x, (ter->get_value(i->x, i->y) + 0.5), i->y);
		if (i != MVector.end())
		{
			i--;
			glVertex3f(i->x, (ter->get_value(i->x, i->y) + 0.5), i->y);
			i++;
		}
		else
		{
			glVertex3f(i->x, (ter->get_value(i->x, i->y) + 0.51), i->y);
		}

	}

	glEnd();

	//glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);
	glUseProgram(0);

}

void dynamicEntity::draw(int shadertype)
{
	// draw only when unit is visible and not sleeping
	if ((visible) && (!sleeping))
	{
		// increase counter (for counting visible objects)
		World::counter += 1;
		// using normal shader for object
		if ((shadertype == SHADER_NORMAL) || (shadertype == SHADER_REFLECTION))
		{

			// get matrix
			glm::mat4 ModelMatrix = glm::mat4(1.0f);
			glm::mat4 ViewMatrix = glm::translate(World::world->getView(), Pos);
			ViewMatrix = glm::rotate(ViewMatrix, r, glm::vec3(0.0f, 1.0f, 0.0f));
			glm::mat4 MVP = World::world->getProjection() * ViewMatrix * ModelMatrix;

			// get light information
			glm::vec3 LightPos = World::world->getLightPos(); // position of light
			glm::vec3 LightDif = World::world->getLightDif(); // diffuse color of light
			glm::vec3 LightAmb = World::world->getLightAmb(); // ambient color of light
			glm::vec3 LightSpec = World::world->getLightSpe(); // specular color of light

			// start shader program

			glUseProgram(shaderID);

			// send matrix to shader
			glUniformMatrix4fv(glGetUniformLocation(shaderID, "MVP"), 1, GL_FALSE,
					&MVP[0][0]);
			glUniformMatrix4fv(glGetUniformLocation(shaderID, "M"), 1, GL_FALSE,
					&ModelMatrix[0][0]);
			glUniformMatrix4fv(glGetUniformLocation(shaderID, "V"), 1, GL_FALSE,
					&ViewMatrix[0][0]);
			glUniform3f(glGetUniformLocation(shaderID, "LightPosition_worldspace"),
					LightPos.x, LightPos.y, LightPos.z);
			glUniform3f(glGetUniformLocation(shaderID, "lightAmb"), LightAmb.x,
					LightAmb.y, LightAmb.z);
			glUniform3f(glGetUniformLocation(shaderID, "lightDif"), LightDif.x,
					LightDif.y, LightDif.z);
			glUniform3f(glGetUniformLocation(shaderID, "lightSpec"), LightSpec.x,
					LightSpec.y, LightSpec.z);

			glUniform1i(glGetUniformLocation(shaderID, "tex0"), 0);

			// draw model

			// TEST animation
			if (Moving)
			{
				mPool->drawModelByID(modelID[(int) animationPos]);
			}
			else
			{
				mPool->drawModelByID(modelID[0]);
			}

			//----------------------------

			glUseProgram(0);

			drawMarker(1.0); // draw marker

		}
		// using shader for shadows
		else if (shadertype == SHADER_SHADOW)
		{
			// get shadow matrix
			glm::mat4 ModelMatrix = glm::mat4(1.0f);
			glm::mat4 ViewMatrix = glm::translate(World::world->getShadowView(),
					glm::vec3(Pos.x, Pos.y, Pos.z));
			ViewMatrix = glm::rotate(ViewMatrix, r, glm::vec3(0.0f, 1.0f, 0.0f));
			glm::mat4 MVP = World::world->getShadowProjection() * ViewMatrix
					* ModelMatrix;

			glUseProgram(shadowShaderID);

			// send matrix to shader
			glUniformMatrix4fv(glGetUniformLocation(shadowShaderID, "depthMVP"), 1,
					GL_FALSE, &MVP[0][0]);

			glUniform1i(glGetUniformLocation(shadowShaderID, "tex0"), 0);

			if (Moving)
			{
				mPool->drawModelByID(modelID[(int) animationPos]);
			}
			else
			{
				mPool->drawModelByID(modelID[0]);
			}

			glUseProgram(0);

		}
	}
	//drawPath(MoveVector);
}

void dynamicEntity::updateAnimation()
{
	if (Moving)
	{
		animationPos = animationPos + 0.75;

		if (animationPos > modelID.size() - 1)
			animationPos = 1.0;
	}
	else
		animationPos = 1.0;
}

void dynamicEntity::control(float t)
{
	if (sleeping)
		return;

	if (NewTarget == true)
	{

		MoveVector = pathfinder->getPath(tar, get2dPosition());
		resFlag = true;
		newMove = true;
		NewTarget = false;

	}

	if (MoveVector.size() > 0)
	{
		Moving = true;
		move();

	}
	else
	{
		Moving = false;
		status = STATUS_READY;

	}

	updateAnimation();
	visible = World::world->SphereInFrustum(Pos, 1.0);
}

bool dynamicEntity::isMooving()
{
	return Moving;
}

